﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;
using Budget.Silver.BalanceServiceReference;
using Budget.Silver.Core.Extensions;
using Budget.Silver.Core.Messages;
using Castle.Core;
using GalaSoft.MvvmLight.Command;
using MvvmValidation;

namespace Budget.Silver.ViewModel
{
    [Transient]
    public class BalanceViewModel : ValidatableViewModel
    {
        #region Private members

        private readonly BalanceServiceClient service;

        private decimal currentBalance;
        private decimal amount;
        private string amountString;
        private ObservableCollection<BalanceData> balances;

        protected override void AddValidationRules()
        {
            Validator.AddRequiredRule(() => AmountString, "Required!");
            Validator.AddRule(() => AmountString, () => RuleResult.Assert(AmountString.TryParseToDecimal(out amount), "Is not a number!"));
            Validator.AddRule(() => AmountString, () => RuleResult.Assert(amount > 0, "Incorrect amount!"));
        }

        private void AddCompletedEvents()
        {
            service.GetBalancesCompleted += ServiceOnGetBalancesCompleted;
            service.SetBalanceCompleted += ServiceOnSetBalanceCompleted;
            service.GetCurrentBalanceCompleted += ServiceOnGetCurrentBalanceCompleted;
        }

        private void ServiceOnGetCurrentBalanceCompleted(object sender, GetCurrentBalanceCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                CurrentBalance = e.Result;
            }
            else
            {
                MessengerInstance.Send(new ExceptionMessage(e.Error));
            }

            MessengerInstance.Send(new BusyMessage(false));
        }

        private void ServiceOnSetBalanceCompleted(object sender, AsyncCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                // do nothing
            }
            else
            {
                MessengerInstance.Send(new ExceptionMessage(e.Error));
            }

            MessengerInstance.Send(new BusyMessage(false));
        }

        private void ServiceOnGetBalancesCompleted(object sender, GetBalancesCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                Balances = e.Result.SortByDescending(b => b.CreationDate);

                service.GetCurrentBalanceAsync();
            }
            else
            {
                MessengerInstance.Send(new BusyMessage(false));
                MessengerInstance.Send(new ExceptionMessage(e.Error));
            }
        }

        private void CreateCommands()
        {
            SetBalance = new RelayCommand(SetBalanceCommand);
        }

        private void SetBalanceCommand()
        {
            var lostMoney = currentBalance != 0 ? currentBalance - amount : 0;
            
            var balance = new BalanceData
                {
                    Amount = amount,
                    LostMoney = lostMoney,
                    CreationDate = DateTime.Now
                };

            CurrentBalance = balance.Amount;
            RaisePropertyChanged(() => CurrentBalance);

            Balances.Insert(0, balance);
            RaisePropertyChanged(() => Balances);

            service.SetBalanceAsync(balance);
        }

        #endregion

        #region Public members

        public decimal CurrentBalance
        {
            get { return currentBalance; }
            private set
            {
                if (!Equals(currentBalance, value))
                {
                    currentBalance = value;

                    RaisePropertyChanged(() => CurrentBalance);
                }
            }
        }

        public ObservableCollection<BalanceData> Balances
        {
            get { return balances; }
            private set
            {
                balances = value;

                RaisePropertyChanged(() => Balances);
            }
        }

        public string AmountString
        {
            get { return amountString; }
            set
            {
                if (!Equals(amountString, value))
                {
                    amountString = value;

                    amount = 0;
                    if (!amountString.TryParseToDecimal(out amount))
                    {
                        RaisePropertyChanged(() => IsReady);
                    }

                    ValidateAll();

                    amount = amountString.ParseToDecimal();

                    RaisePropertyChanged(() => IsReady);
                }
            }
        }

        public ICommand SetBalance { get; private set; }

        public bool IsReady
        {
            get { return amount > 0; }
        }

        public BalanceViewModel(BalanceServiceClient service)
        {
            this.service = service;

            AddCompletedEvents();
            CreateCommands();
            
            balances = new ObservableCollection<BalanceData>();

            service.GetBalancesAsync();
            MessengerInstance.Send(new BusyMessage(true));
        }

        #endregion
    }
}